//===============================================================================
// Microsoft Architecture Strategy Team
// LitwareHR - SaaS Sample Application
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================


//===============================================================================
// Microsoft Architecture Strategy Team
// LitwareHR - S+S Sample Application
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Collections.Generic;
using System.Text;

using System.Data;
using System.Data.Common;

using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.Data.SqlCe;
using Microsoft.Practices.ObjectBuilder;
using Microsoft.Practices.CompositeUI;

using System.Xml;
using System.Xml.Serialization;
using System.Threading;

using System.IO;

using Shp.Runtime.Contract;
using Microsoft.Practices.SmartClient.ConnectionMonitor;
using Microsoft.Practices.SmartClient.ConnectionMonitor.Implementations;
using Microsoft.Practices.CompositeUI.EventBroker;
using LitwareHR.PublicApp.WinUX.Constants;
using LitwareHR.Infrastructure.Interface;
using LitwareHR.Infrastructure.Interface.Constants;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
namespace LitwareHR.PublicApp.WinUX.Services
{
    public class DefSynchronizationService:IDisposable 
    {
    
        private ServiceProxy.UnSecure.DataModelService dataModelService;
        private AuthenticationService authnService;
        private ConnectionMonitor connectionMonitor = null;
        private RequestsManagerService requestManagerService = null;
        private Timer timer;
        private OfflineDataService offlineService = null;
        private RecruitingService recruitingService = null;
        private delegate void ExecuteSynchAllModelsDelegate();
        private ExecuteSynchAllModelsDelegate executeSynchAllModelsDelegate = null;
        
        [EventSubscription(EventTopicNames.ConnectionStatusChanged, ThreadOption.Background)]
        public void OnConnectionStatusChanged(object sender, EventArgs<bool> e)
        {
            if (executeSynchAllModelsDelegate != null)
                    executeSynchAllModelsDelegate();                                   
        }

        public void ExecuteSynchAllModels()
        {
            SynchAllModels(); 
        }
        /// <summary>
        /// Notification of chnages to the Metamodel Definitions
        /// </summary>
        [EventPublication(EventTopicNames.MetaModelUpdated, PublicationScope.Global)]
        public event EventHandler<MetaModelUpdatedEventArgs> MetaModelChanged;

        /// <summary>
        /// Notification of chnages to the Metamodel Definitions
        /// </summary>
        [EventPublication(EventTopicNames.MetaModelUpdating, PublicationScope.Global)]
        public event EventHandler<MetaModelUpdatedEventArgs> MetaModelChanging;
        
        [InjectionConstructor]
        public DefSynchronizationService([ServiceDependency] ServiceProxy.UnSecure.DataModelService dataModelService, 
                                         [ServiceDependency] OfflineDataService offlineService,
                                         [ServiceDependency] AuthenticationService authnService,
                                         [ServiceDependency] ConnectionMonitor connectionMonitor,
                                         [ServiceDependency] RequestsManagerService requestManagerService,
                                         [ServiceDependency] RecruitingService recruitingService ) 
        {
            this.dataModelService = dataModelService;
            this.authnService = authnService;
            this.connectionMonitor = connectionMonitor;
            this.requestManagerService = requestManagerService;
            this.offlineService = offlineService;
            this.recruitingService = recruitingService;
        }

        public void Initialize()
        {
            //Synchronize on Initialization
            SynchAllModels();

            // Setup the "timer to get automatic updates of Data
            timer = new Timer(new TimerCallback(GetSyncTimerCallback), null, 2000, Timeout.Infinite);            
        }

        /// <summary>
        /// Timer Callback
        /// </summary>
        /// <param name="o"></param>
        private void GetSyncTimerCallback(object o)
        {
            timer.Change(Timeout.Infinite, Timeout.Infinite);
            SynchAllModels();
            timer.Change(2000, Timeout.Infinite);         
        }

        /// <summary>
        /// SynchAll Model
        /// </summary>
        public void SynchAllModels()
        {
            if (!connectionMonitor.IsConnected)
                return;
            try
            {
                executeSynchAllModelsDelegate = null;
                SynchronizeDataModelDef();
                SyncronizeData();
                executeSynchAllModelsDelegate = new ExecuteSynchAllModelsDelegate(this.ExecuteSynchAllModels);

            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, "Default Policy");
                //Exception wont be rethrown.
            }
        }

        private void SyncronizeData()
        {
            recruitingService.SyncronizeData();
        }
        
        /// <summary>
        /// Checks for changes on locally stored Entity definition against the current definition 
        /// stored in LitwareHR
        /// </summary>
        /// <param name="entityName">Name of the Entity</param>
        /// <param name="entityDef">Definition if the Entity</param>
        private void SynchronizeEntityDef(string entityName, EntityField[] entityDef)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(EntityField[]));
            MemoryStream stream = new MemoryStream();
            serializer.Serialize(stream, entityDef);
            StreamReader reader = new StreamReader(stream);
            stream.Position = 0;

            EntityField[] currentEntityDef = entityDef;
            string currentEntityDefStr = reader.ReadToEnd();
            string localEntityDefStr = offlineService.GetSerializedEntityDef(entityName);

            if (localEntityDefStr == null)
            {
                //Create Entity Def
                offlineService.CreateEntityDef(entityName, currentEntityDefStr, entityDef);
                return; 
            }

            if (currentEntityDefStr != localEntityDefStr)
            {
                OnMetaModelChanging(MetaModelUpdatedEventArgs.UpdatedModel.Data, entityName);

                try
                {
                    EntityField[] localEntityDef = offlineService.GetEntityDef(entityName);

                    // If there is any Schema Change then Disconnect and then do Update in Local Entity Diff
                    DesktopConnection desktopConnection = (DesktopConnection)connectionMonitor.Connections[0];
                    desktopConnection.Disconnect();

                    //Getting the Schema Chnages done from this method
                    offlineService.AlterEntityDef(localEntityDef, currentEntityDef, entityName, currentEntityDefStr);

                    //Move Open Position Message from Request Queue to Dead Queue.
                    //In current application there is only one entity "Position". If this entity is changed
                    // we need to block all messages with tag "AddInstances". If there are more than one entity
                    //than based on entity which has been modified, we would need to decided which messages to hold back.
                    requestManagerService.MoveQueuedRequestToDeadLetterQueue("AddInstance");

                    //After updating the Local Enity the Connect again
                    desktopConnection.Connect();
                }
                catch (Exception ex)
                {
                    bool rethrow = ExceptionPolicy.HandleException(ex, "Default Policy");
                    if (rethrow)
                    {
                        throw;
                    }
                }

                OnMetaModelChanged(MetaModelUpdatedEventArgs.UpdatedModel.Data, entityName);
            }
        }
        
        /// <summary>
        /// Synchronizes local data model definitions against those stored in LitwareHR
        /// </summary>
        public void SynchronizeDataModelDef()
        {
            Dictionary<Guid, string> entityDefList = dataModelService.GetEntityDefList();
            Dictionary<Guid, string>.Enumerator iterator = entityDefList.GetEnumerator();
            while (iterator.MoveNext())
            {
                EntityField[] entityFlds = dataModelService.GetEntityDefFields(authnService.CurrentTenantAlias, iterator.Current.Value);
                SynchronizeEntityDef(iterator.Current.Value.Trim(), entityFlds);
            }
        }

        /// <summary>
        /// Raise MetaModelChanging Event
        /// </summary>
        /// <param name="item"></param>
        /// <param name="itemName"></param>
        private void OnMetaModelChanging(MetaModelUpdatedEventArgs.UpdatedModel item, string itemName)
        {
            if (MetaModelChanging != null)
            {
                MetaModelChanging(this, new MetaModelUpdatedEventArgs(item, itemName));
            }
        }

        /// <summary>
        /// Raise MetaModelChange Event
        /// </summary>
        /// <param name="item"></param>
        /// <param name="itemName"></param>
        private void OnMetaModelChanged( MetaModelUpdatedEventArgs.UpdatedModel item, string itemName)
        {
            if (MetaModelChanged != null)
            {
                MetaModelChanged(this, new MetaModelUpdatedEventArgs(item, itemName));           
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            timer.Change(Timeout.Infinite, Timeout.Infinite);
            timer.Dispose();
        }

        #endregion
    }
}
